Skip to content

fix(spawn): publish owned subprocess completion before retirement checks - #147

Merged
steipete merged 4 commits into
mainfrom
claude/subprocess-retirement-publication
Oct 8, 2026
Merged

steipete merged 4 commits into
mainfrom
claude/subprocess-retirement-publication

Conversation

@steipete

@steipete steipete commented Oct 8, 2026 •

Copy link
Copy Markdown

After a blocked I/O callback outlasts a retirement deadline, the kernel may already have completed a child and its pipes while a deferred observer still sees stale completion state. This publishes owned subprocess completion before that observer without polling unrelated I/O or moving global event-loop phases.

Supersedes #142 by publishing only owned subprocess completion while preserving global phase order; #142 remains open for the coordinator.

The process-only kqueue/epoll snapshot retains ready owners before task dispatch and publishes them afterward. Existing readers deliver trailing bytes and real EOF; EAGAIN is never synthetic EOF. Extra stdio pipes count toward child close. The secondary descriptor closes when empty, stays above standard fds, and preserves the primary watch if additional watcher resources are exhausted.

HTTP/2 fixtures now bind to their IPv4 clients' address, and the stale-fd test selects the actual stdout socket rather than Darwin's FIFO-shaped kqueue descriptor. Assertions, deadlines, timeouts and skip conditions are unchanged.

Validation

Gate Result
Original OpenClaw retirement, original deadline and six-second stall Node passes; main fails; candidate 50/50 on Darwin and Linux
Reviewer-authored Node 24 controls IO 10/10; stdio/held-pipe 50/50 on both
General ordering / nested reentry All seven controls exactly match main; reentry 50/50 on both
Surrounding suites 110 files plus worker destruction on both; only intended regression improvement and matching baseline failures
Previously broken critical suites Timers 24/24, HTTP2 lifecycle including synchronous POST plus server.stop(true) 23/23, HTTPS Agent reuse 30/30, TLS Duplex teardown 68/68
75-child Linux readiness Node 75 exits, main zero, candidate 75; kernel/cleanup preconditions pass
Broker consumer Candidate 14/14 on both; main passes the other 13
Isolated Gateway smoke Node/main/candidate valid; all turns accounted for, zero errors/pending, accepted shutdown, exit 0, no forced cleanup
Rust / static checks / review Rust 12/12; Clippy, TypeScript, format/source lints, Miri and Mordant green; P2 scoped-clean

Exact-head Linux/Darwin CI is green (27/27 and 21/21 report rows). Exact-head Windows CI is green for x64 and ARM64 builds, smoke and 31/31 compatibility rows each. Native artifact commit files/checksums match this head; the CI-built Darwin executable also passes the original consumer retirement fixture. Windows dispatch was a tagless dry run and Publish was skipped.

Manual comparisons retain one matching baseline failure per platform: native-Node TLS session resumption on Darwin, and an empty SHELL in the curated Linux environment. Main additionally fails the new regression. No failure was filtered or retried into a passing result.

Earlier failures and corrections are retained. An early Darwin HTTP2 run had 21 timeouts on a shared listener without listener-identity tracing, so its exact cause remains unproven. Controlled probes reproduced overlapping IPv4/IPv6 listeners; fresh main and candidate each passed all 84 original unchanged cases. No HTTP2 runtime workaround or tolerance change is included.

Related upstream work checked: oven-sh#33614, oven-sh#43888 and oven-sh#37869. None supplies this process-only undispatched-readiness mechanism; their broader scheduling or synthetic-EOF behavior is not imported.

Ready for coordinator review; intentionally unmerged. No release, tag, runtime-pin update or live/Team Gateway operation was performed.

Capture ready subprocess owners before tasks, publish terminal state afterward, and drain only their owned pipes while preserving global phase order. Count extra stdio pipes for child close and preserve descriptor exhaustion behavior. Add a Node-qualified retirement regression and bind HTTP/2 conformance fixtures to their client address.
@steipete
steipete marked this pull request as ready for review October 8, 2026 13:33
@steipete
steipete merged commit bf099fb into main Oct 8, 2026
23 of 25 checks passed
@steipete
steipete deleted the claude/subprocess-retirement-publication branch October 8, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant